home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume7 / nag / part02 < prev    next >
Encoding:
Internet Message Format  |  1986-11-30  |  45.5 KB

  1. Subject:  v07i063:  Nag reminder service, Part02/02
  2. Newsgroups: mod.sources
  3. Approved: mirror!rs
  4.  
  5. Submitted by: rtech!daveb@rtech.uucp (Dave Brower)
  6. Mod.sources: Volume 7, Issue 63
  7. Archive-name: nag/Part02
  8.  
  9. #!/bin/sh
  10. # This is a shell archive, meaning:
  11. # 1. Remove everything above the #!/bin/sh line.
  12. # 2. Save the resulting text in a file.
  13. # 3. Execute the file with /bin/sh (not csh) to create the files:
  14. #    gdate.c
  15. #    nag.c
  16. export PATH; PATH=/bin:$PATH
  17. echo shar: extracting "'gdate.c'" '(13521 characters)'
  18. if test -f 'gdate.c'
  19. then
  20.     echo shar: over-writing existing file "'gdate.c'"
  21. fi
  22. sed 's/^X//' << \SHAR_EOF > 'gdate.c'
  23. X/*
  24. X * routines to turn a date from various formats to other formats
  25. X *
  26. X *    Primary interesting routine is gdate() which eats a date
  27. X *    string of several common formats (see comment) and
  28. X *    fills in a standard UNIX tm structure.
  29. X *
  30. X *    Barry Shein, Boston University
  31. X *
  32. X *    if you compile it -DDEBUG (with DEBUG defined) it will
  33. X *    pull in a main() routine to run standalone for testing.
  34. X *
  35. X *    NOTE:
  36. X *
  37. X *    Barry's gdate was broken by a 1-off error; tm_mon is kept
  38. X *    in the range 0..11 instead of 1..12.  Also, his totime was
  39. X *    broken, so I've deleted it and use the tm_to_time() function
  40. X *    from mod.sources.
  41. X *
  42. X *
  43. X * Defines the functions:
  44. X *
  45. X * lcase() -- convert a char to lower case
  46. X * dstring() -- get digit string from sp into bp (buffer) returning new sp
  47. X * skipw() -- skip white space returning updated ptr
  48. X * prefix() -- return how many chars of s1 prefix s2
  49. X * find() -- look up str in list for non-ambiguous (prefix) match
  50. X * lookup() -- like find but demands exact match
  51. X * extract() -- extract a token
  52. X * fill() -- fill up an area with a value (eg. zeros)
  53. X *
  54. X * gdate() -- convert a date/time string to a tm structure.
  55. X * gtime() -- convert time string to a tm structure.
  56. X *
  57. X * days() -- how many days were in a year.
  58. X * jan1() -- return day of the week of jan 1 of given year
  59. X * dowk() -- insert day of week given year and day of year into tm struct.
  60. X * doyr() -- insert partial day of year given yr, mon and mday into tm struct.
  61. X *
  62. X * leap() -- Return 1 if `y' is a leap year, 0 otherwise.
  63. X * ndays() -- number of days between UNIX epoch and time in a tm struct.
  64. X * tm_to_time() -- Convert a tm struct to a time_t.
  65. X */
  66. X#include <stdio.h>
  67. X#include <ctype.h>
  68. X#include <sys/types.h>
  69. X
  70. X#ifdef SYS5
  71. X
  72. X#    define    time_t    long    /* SV is inconsistent, so go with lcd */
  73. X
  74. X#    include <time.h>
  75. X#    include <sys/times.h>
  76. X#    include <string.h>
  77. X
  78. X# else                /* BSD */
  79. X
  80. X#    include <sys/time.h>
  81. X#    include <sys/timeb.h>
  82. X#    include <strings.h>
  83. X
  84. X#endif
  85. X
  86. X/*----------------------------------------------------------------
  87. X *
  88. X * Manifest constants
  89. X *
  90. X */
  91. X
  92. X#define MAXTOK 20
  93. X#define AMBIG  -1    /* ambiguous month */
  94. X#define FALSE  -2    /* bad syntax       */
  95. X
  96. X/*----------------------------------------------------------------
  97. X *
  98. X *    static and global Data
  99. X *
  100. X */
  101. X
  102. Xchar *months[] = {
  103. X  "january", "february", "march", "april", "may", "june", "july",
  104. X  "august", "september", "october", "november", "december", 0
  105. X} ;
  106. X  
  107. Xchar *dow[] = {
  108. X    "sunday", "monday", "tuesday", "wednesday", "thursday",
  109. X    "friday", "saturday", 0
  110. X} ;
  111. X    
  112. X    /*
  113. X     *    known time-zone name list
  114. X     */
  115. Xchar *tz[] =
  116. X{
  117. X  "adt", "ast", "edt", "est", "cdt", "cst", "mst", "mdt",
  118. X  "pdt", "pst", "gmt",
  119. X  0
  120. X} ;
  121. X
  122. Xchar mdays[] = {
  123. X  31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  124. X} ;
  125. X
  126. X/*----------------------------------------------------------------
  127. X *
  128. X *    Utility functions
  129. X *
  130. X */
  131. X
  132. X/*
  133. X * lcase() -- convert a char to lower case
  134. X */
  135. Xlcase(c) register char c ;
  136. X{
  137. X  return(isupper(c) ? tolower(c) : c) ;
  138. X}
  139. X
  140. X/*
  141. X * dstring() -- get digit string from sp into bp (buffer) returning new sp
  142. X */
  143. Xchar *
  144. Xdstring(bp,sp)
  145. X     register char *sp, *bp  ;
  146. X{
  147. X  register int i = 0 ;
  148. X  while(isdigit(*sp))
  149. X    if(i++ == MAXTOK) break ;
  150. X    else *bp++ = *sp++ ;
  151. X  *bp = '\0' ;
  152. X  return(sp) ;
  153. X}
  154. X
  155. X/*
  156. X * skipw() -- skip white space returning updated ptr
  157. X */
  158. Xchar *
  159. Xskipw(sp) register char *sp ;
  160. X{
  161. X  while(isspace(*sp) || *sp == '-') ++sp ;
  162. X  return(sp) ;
  163. X}
  164. X
  165. X/*
  166. X * prefix() -- return how many chars of s1 prefix s2
  167. X */
  168. Xprefix(s1,s2) register char *s1, *s2 ;
  169. X{
  170. X  register int i = 0  ;
  171. X  
  172. X  for(; *s1 == *s2 ; s1++,s2++,i++)
  173. X    if(*s2 == '\0') break ;
  174. X  return(*s1 == '\0' ? i : 0) ;
  175. X}
  176. X
  177. X/*
  178. X * find() -- look up str in list for non-ambiguous (prefix) match
  179. X */
  180. Xfind(sp,lp) register char *sp, **lp ;
  181. X{
  182. X  int i,j,ambig = 0 ;
  183. X  register int k ;
  184. X  int ret  = FALSE ;
  185. X  
  186. X  for(i = 0,k = 0 ; *lp ; lp++,k++)
  187. X    if((j  = prefix(sp,*lp)) > i)
  188. X      {
  189. X    ambig = 0 ;
  190. X    i = j ;
  191. X    ret = k + 1 ;
  192. X      }
  193. X    else if(j && (i == j)) ++ambig ;
  194. X  return(ambig ? AMBIG : ret) ;
  195. X}
  196. X
  197. X/*
  198. X * lookup() -- like find but demands exact match
  199. X */
  200. Xlookup(sp,lp) register char *sp, **lp ;
  201. X{
  202. X  int i = 0 ;
  203. X  
  204. X  for(i=0 ; *lp ; lp++,i++)
  205. X    if(strcmp(sp,*lp) == 0) return(i+1) ;
  206. X  return(0) ;
  207. X}
  208. X
  209. X/*
  210. X * extract() -- extract a token
  211. X */
  212. Xchar *
  213. Xextract(bp,sp) register char *bp, *sp ;
  214. X{
  215. X  register int i = 0 ;
  216. X  
  217. X  sp = skipw(sp) ;
  218. X  for(; isalnum(*sp); sp++)
  219. X    if(i++ == MAXTOK) break ;
  220. X    else *bp++ = lcase(*sp) ;
  221. X  *bp = '\0' ;
  222. X  return(sp) ;
  223. X}
  224. X
  225. X/*
  226. X * fill() -- fill up an area with a value (eg. zeros)
  227. X */
  228. Xfill(cp,c,n) register char *cp, c ; register int n ;
  229. X{
  230. X  while(n--) *cp++ = c ;
  231. X}
  232. X
  233. X/*----------------------------------------------------------------
  234. X *
  235. X *    gdate, gtime related.
  236. X *
  237. X */
  238. X
  239. Xchar *gdate() ;
  240. Xchar *gtime() ;
  241. X
  242. X/*
  243. X *    gdate(date_str_ptr,time_buf_ptr)
  244. X *    (see CTIME(3) in UPM for second arg format)
  245. X *    takes many reasonable date strings and translates to
  246. X *    the time-buf structure (integers)
  247. X *
  248. X *    formats (eg.):
  249. X *        oct 19, 1983
  250. X *        OcT 19, 1983 12:43
  251. X *        oCt 19, 1983 12:43:33
  252. X *        oct 19 1983 ...
  253. X *        19 oct 83 ....
  254. X *        10/19/83 12:43:33
  255. X *        19-OCT-83 12:43:00 (DEC style)
  256. X *        Wed Oct 19 12:43 EST 83 (UNIX style)
  257. X *        oct. 19, 83 1:44 pm est
  258. X *        831019124333    (see date(1))
  259. X *    some variations on those themes also.
  260. X *
  261. X *    BUGS: probably a few (maybe some in the eye of the beholder)
  262. X *        does not set dst flag (unless 'EDT' is in string)
  263. X */
  264. Xchar *
  265. Xgdate(sp,tp) register char *sp ; register struct tm *tp ;
  266. X{
  267. X  char buf[MAXTOK] ;
  268. X  
  269. X  fill(tp,'\0',sizeof *tp) ;
  270. X  sp = skipw(sp) ;
  271. X  if(isdigit(*sp))    /* either '7/12/83' or '12 jul 83' */
  272. X    {
  273. X      if(isdigit(sp[1]) && isdigit(sp[2])) /* UNIX yymmddhhmmss */
  274. X    {
  275. X      buf[2] = '\0' ;
  276. X      (void)strncpy(buf,sp,2) ;
  277. X      tp->tm_year = atoi(buf) ;
  278. X      (void)strncpy(buf,sp += 2,2) ;
  279. X      tp->tm_mon = atoi(buf) - 1 ;
  280. X      sp += 2 ;
  281. X      if(!isdigit(*sp)) goto badday ;
  282. X      (void)strncpy(buf,sp,2) ;
  283. X      tp->tm_mday = atoi(buf) ;
  284. X      sp += 2 ;
  285. X      if(!isdigit(*sp)) goto check ;
  286. X      (void)strncpy(buf,sp,2) ;
  287. X      
  288. X      /* ??? formerly null effect "tp->tm_hour ;" */
  289. X      
  290. X      tp->tm_hour = atoi(buf) ;
  291. X      sp += 2 ;
  292. X      if(!isdigit(*sp)) goto check ;
  293. X      (void)strncpy(buf,sp,2) ;
  294. X      tp->tm_min = atoi(buf) ;
  295. X      sp += 2 ;
  296. X      if(!isdigit(*sp)) goto check ;
  297. X      (void)strncpy(buf,sp,2) ;
  298. X      tp->tm_min = atoi(buf) ;
  299. X      goto check ;
  300. X    }
  301. X      sp = dstring(buf,sp) ;
  302. X      sp = skipw(sp) ;
  303. X      if(*sp == '/')    /* must be '7/12/83' */
  304. X    {
  305. X      if((tp->tm_mon = atoi(buf) - 1) < 0 || (tp->tm_mon > 11))
  306. X        {
  307. X          tp->tm_mon = FALSE ;
  308. X          goto badmon ;
  309. X        }
  310. X      sp = skipw(++sp) ;
  311. X      if(!isdigit(*sp)) goto badday ;
  312. X      sp = dstring(buf,sp) ;
  313. X      tp->tm_mday = atoi(buf) ;
  314. X      
  315. X      sp = skipw(sp) ;
  316. X      if(*sp != '/') goto badyr ;
  317. X      sp = skipw(++sp) ;
  318. X      if(!isdigit(*sp)) goto badyr ;
  319. X      sp = dstring(buf,sp) ;
  320. X      tp->tm_year = atoi(buf) ;
  321. X      
  322. X      sp = gtime(sp,tp) ;
  323. X    }
  324. X      else
  325. X    {
  326. X      /*
  327. X       * must be '12 jul 83'
  328. X       */
  329. X      tp->tm_mday = atoi(buf) ;
  330. X      
  331. X      sp = extract(buf,sp) ;
  332. X      if((tp->tm_mon = find(buf,months)) < 0) goto badmon ;
  333. X      
  334. X      if(*sp == '.') ++sp ;
  335. X      sp = skipw(sp) ;
  336. X      
  337. X      if(!isdigit(*sp)) goto badyr ;
  338. X      sp = dstring(buf,sp) ;
  339. X      tp->tm_year = atoi(buf) ;
  340. X      
  341. X      sp = gtime(sp,tp) ;
  342. X    }
  343. X    }
  344. X  else
  345. X    {
  346. X      int flag = 0 ;    /* used to indicate looking for UNIX style */
  347. X      
  348. X      /*
  349. X       * either 'jul 12 83' or (UNIX) Wed jul 12 18:33 EST 1983
  350. X       */
  351. X      sp = extract(buf,sp) ;
  352. X      if(find(buf,dow) > 0)
  353. X    {
  354. X      sp = extract(buf,sp) ;
  355. X      ++flag ;
  356. X    }
  357. X      
  358. X      if((tp->tm_mon = find(buf,months)) < 0) goto badmon ;
  359. X      
  360. X      if(*sp == '.') ++sp ;
  361. X      sp = skipw(sp) ;
  362. X      
  363. X      if(!isdigit(*sp)) goto badday ;
  364. X      sp = dstring(buf,sp) ;
  365. X      tp->tm_mday = atoi(buf) ;
  366. X      
  367. X      sp = skipw(sp) ;
  368. X      if(*sp == ',') sp++ ;
  369. X      sp = skipw(sp) ;
  370. X      
  371. X      if(flag) sp = skipw(gtime(sp,tp)) ;
  372. X      
  373. X      if(!isdigit(*sp)) goto badyr ;
  374. X      sp = dstring(buf,sp) ;
  375. X      tp->tm_year = atoi(buf) ;
  376. X      
  377. X      if(!flag) sp = gtime(sp,tp) ;
  378. X    }
  379. X check:
  380. X  /*
  381. X   * check for ridiculous numbers
  382. X   */
  383. X  if(tp->tm_mday < 1) goto badday ;
  384. X  if(tp->tm_mday > mdays[tp->tm_mon])
  385. X    if(!((tp->tm_mon == 1) &&    /* check for Feb 29 */
  386. X     (tp->tm_mday == 29) && (days(tp->tm_year) == 365) ))
  387. X      goto badday ;
  388. X  if(tp->tm_year >= 1900) tp->tm_year -= 1900 ;
  389. X  if(tp->tm_hour > 23)
  390. X    {
  391. X      tp->tm_hour = FALSE ;
  392. X      return(sp) ;
  393. X    }
  394. X  if(tp->tm_min > 59)
  395. X    {
  396. X      tp->tm_hour = FALSE ;
  397. X      return(sp) ;
  398. X    }
  399. X  if(tp->tm_sec > 59)
  400. X    {
  401. X      tp->tm_sec = FALSE ;
  402. X      return(sp) ;
  403. X    }
  404. X  /*
  405. X   *    fill in day of year, day of week (these calls must be
  406. X   *    in this order as dowk() needs doyr()
  407. X   */
  408. X  
  409. X  doyr(tp) ;
  410. X  dowk(tp) ;
  411. X  /*
  412. X   * all done !
  413. X   */
  414. X  return(NULL) ;
  415. X  badday :
  416. X  tp->tm_mday = FALSE ;
  417. X  return(sp) ;
  418. X  badmon :
  419. X  return(sp) ;
  420. X  badyr :
  421. X  tp->tm_year = FALSE ;
  422. X  return(sp) ;
  423. X}
  424. X
  425. X/*
  426. X *  gtime() -- get  hh:mm:ss or equivalent into a tm struct.
  427. X */
  428. Xchar *
  429. Xgtime(sp,tp) register char *sp ; register struct tm *tp ;
  430. X{
  431. X  char buf[MAXTOK],*cp ;
  432. X  
  433. X  sp = skipw(sp) ;
  434. X  if(isdigit(*sp))
  435. X    {
  436. X      sp = dstring(buf,sp) ;
  437. X      tp->tm_hour = atoi(buf) ;
  438. X      sp = skipw(sp) ;
  439. X      if(*sp == ':') sp = skipw(++sp) ;
  440. X      else goto out ;
  441. X      if(isdigit(*sp))
  442. X    {
  443. X      sp = dstring(buf,sp) ;
  444. X      tp->tm_min = atoi(buf) ;
  445. X      sp = skipw(sp) ;
  446. X      if(*sp == ':') sp = skipw(++sp) ;
  447. X      else goto out ;
  448. X      if(isdigit(*sp))
  449. X        {
  450. X          sp = dstring(buf,sp) ;
  451. X          tp->tm_sec = atoi(buf) ;
  452. X        }
  453. X    }
  454. X    }
  455. X  out :
  456. X  sp = skipw(sp) ;
  457. X  if(isalpha(*sp))    /* PM:AM or time zone or both */
  458. X    {
  459. X      cp = extract(buf,sp) ;
  460. X      if(strcmp(buf,"am") == 0 || strcmp(buf,"pm") == 0)
  461. X    {
  462. X      if(buf[0] == 'p' && tp->tm_hour < 12)
  463. X        tp->tm_hour += 12 ;
  464. X      sp = cp = skipw(cp) ;
  465. X      cp = extract(buf,cp) ;
  466. X    }
  467. X      if(lookup(buf,tz))
  468. X    {
  469. X      if(buf[1] == 'd') tp->tm_isdst++ ;
  470. X      sp = skipw(cp) ;
  471. X    }
  472. X    }
  473. X  return (sp);    
  474. X}
  475. X
  476. X/*
  477. X * days() -- how many days were in a year.
  478. X *
  479. X *    Ok, you were all wondering so here it is folks...
  480. X *    THE LEAP YEAR CALCULATION
  481. X *    note: does not take into account 1752.
  482. X */
  483. Xdays(y) register int y ;
  484. X{
  485. X  if(y < 1970) y += 1900 ;
  486. X  if(((y % 4) == 0) && ( (y % 100) || ((y % 400)==0) )) y = 366 ;
  487. X  else y = 365 ;
  488. X  return(y) ;
  489. X}
  490. X
  491. X
  492. X/*
  493. X * jan1() -- return day of the week of jan 1 of given year
  494. X */
  495. Xjan1(yr)
  496. X{
  497. X  register y, d;
  498. X  
  499. X  /*
  500. X   *    normal gregorian calendar
  501. X   *    one extra day per four years
  502. X   */
  503. X  
  504. X  y = yr;
  505. X  d = 4+y+(y+3)/4;
  506. X  
  507. X  /*
  508. X   *    julian calendar
  509. X   *    regular gregorian
  510. X   *    less three days per 400
  511. X   */
  512. X  
  513. X  if(y > 1800) {
  514. X    d -= (y-1701)/100;
  515. X    d += (y-1601)/400;
  516. X  }
  517. X  
  518. X  /*
  519. X   *    take care of weirdness at 1752.
  520. X   */
  521. X  
  522. X  if(y > 1752)
  523. X    d += 3;
  524. X  
  525. X  return(d%7);
  526. X}
  527. X
  528. X/*
  529. X * dowk() -- insert day of week given year and day of year into tm struct.
  530. X */
  531. Xdowk(tp) register struct tm *tp ;
  532. X{
  533. X  tp->tm_wday = (jan1(tp->tm_year+1900) + tp->tm_yday) % 7 ;
  534. X}
  535. X
  536. X/*
  537. X * doyr() -- insert partial day of year given yr and mon into tm struct.
  538. X */
  539. Xdoyr(tp) register struct tm *tp ;
  540. X{
  541. X  register int i,j ;
  542. X
  543. X  j = ((tp->tm_mon > 1) && (days(tp->tm_year) == 366)) ? 1 : 0 ;
  544. X  for(i=1 ; i < tp->tm_mon ; i++)
  545. X    j += mdays[i-1] ;
  546. X  tp->tm_yday = j + tp->tm_mday - 1 ;
  547. X}
  548. X
  549. X/*----------------------------------------------------------------
  550. X *
  551. X * tm_to_time related
  552. X *
  553. X */
  554. X
  555. X/*
  556. X * leap() -- Return 1 if `y' is a leap year, 0 otherwise.
  557. X */
  558. Xstatic int
  559. Xleap (y)
  560. X     int y;
  561. X{
  562. X  y += 1900;
  563. X  if (y % 400 == 0)
  564. X    return (1);
  565. X  if (y % 100 == 0)
  566. X    return (0);
  567. X  return (y % 4 == 0);
  568. X}
  569. X
  570. X/*
  571. X * ndays() -- number of days since UNIX epoch and time in a tm struct.
  572. X */
  573. Xstatic int
  574. Xndays (p)
  575. X     struct tm *p;
  576. X{
  577. X  register n = p->tm_mday;
  578. X  register m, y;
  579. X  register char *md = "\37\34\37\36\37\36\37\37\36\37\36\37";
  580. X  
  581. X  for (y = 70; y < p->tm_year; ++y) {
  582. X    n += 365;
  583. X    if (leap (y)) ++n;
  584. X  }
  585. X  for (m = 0; m < p->tm_mon; ++m)
  586. X    n += md[m] + (m == 1 && leap (y));
  587. X  return (n);
  588. X}
  589. X
  590. X/*
  591. X * tm_to_time() -- Convert a tm struct to a time_t.
  592. X *
  593. X * returns 0 if the time is before the UNIX epoch, 1/1/70 00:00:00
  594. X */
  595. Xtime_t
  596. Xtm_to_time (tp)
  597. X     struct tm *tp;
  598. X{
  599. X  register int m1, m2;
  600. X  time_t t;
  601. X  struct tm otm;
  602. X  
  603. X  /* special case date before epoch */
  604. X  if( tp->tm_year < 70) return(0);
  605. X  
  606. X  t = (ndays (tp) - 1) * 86400L + tp->tm_hour * 3600L
  607. X    + tp->tm_min * 60 + tp->tm_sec;
  608. X  /*
  609. X   * Now the hard part -- correct for the time zone:
  610. X   */
  611. X  otm = *tp;
  612. X  tp = localtime (&t);
  613. X  m1 = tp->tm_hour * 60 + tp->tm_min;
  614. X  m2 = otm.tm_hour * 60 + otm.tm_min;
  615. X  t -= ((m1 - m2 + 720 + 1440) % 1440 - 720) * 60L;
  616. X  return (t);
  617. X}
  618. X
  619. X/*----------------------------------------------------------------
  620. X *
  621. X * Test program related
  622. X *
  623. X */
  624. X
  625. X#if DEBUG
  626. X/*
  627. X *  test driver
  628. X *    translates first arg from command line (argv[1])
  629. X *    and dumps out structure built.
  630. X */
  631. Xusage(sp) char *sp ;
  632. X{
  633. X  fprintf(stderr,"Usage: %s date\n",sp) ;
  634. X  exit(1) ;
  635. X}
  636. X
  637. X/*
  638. X * main() -- test the gdate and tm_to_time routines
  639. X */
  640. Xmain(argc,argv) int argc ; char **argv ;
  641. X{
  642. X  char *cp ;
  643. X  struct tm tm ;
  644. X  time_t t,t2 ;
  645. X  char *asctime();
  646. X  char *ctime();
  647. X  
  648. X  if(argc != 2) usage(*argv) ;
  649. X  
  650. X  if((cp = gdate(*++argv,&tm)) != NULL)
  651. X    printf("error: %s (%s)\n",*argv,cp) ;
  652. X  
  653. X  printf("year : %d month: %d day: %d\n",
  654. X     tm.tm_year,tm.tm_mon,tm.tm_mday);
  655. X  printf("day of month: %d hour: %d minute: %d second: %d\n",
  656. X     tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec) ;
  657. X  printf("day of year: %d day of week: %d dst: %d\n",
  658. X     tm.tm_yday, tm.tm_wday, tm.tm_isdst) ;
  659. X  
  660. X  t = time(NULL) ;
  661. X  t2 = tm_to_time(&tm) ;
  662. X  
  663. X  printf("time_t of now %d, of arg %d\n",t, t2 ) ;
  664. X  printf("Now:  %s", ctime(&t) );
  665. X  printf("Arg:  %s", ctime(&t2) );
  666. X  exit(0) ;
  667. X}
  668. X
  669. X#endif    /* DEBUG */
  670. X
  671. SHAR_EOF
  672. if test 13521 -ne "`wc -c 'gdate.c'`"
  673. then
  674.     echo shar: error transmitting "'gdate.c'" '(should have been 13521 characters)'
  675. fi
  676. echo shar: extracting "'nag.c'" '(29846 characters)'
  677. if test -f 'nag.c'
  678. then
  679.     echo shar: over-writing existing file "'nag.c'"
  680. fi
  681. sed 's/^X//' << \SHAR_EOF > 'nag.c'
  682. X/*----------------------------------------------------------------
  683. X *
  684. X * Nag.c -- annoying reminder service daemon
  685. X *
  686. X * Sun Aug 24 14:18:08 PDT 1986
  687. X *
  688. X * by Dave Brower, {amdahl, cbosgd, mtxinu, sun}!rtech!gonzo!daveb
  689. X *
  690. X * Copyright 1986, David C Brower.  All rights reserved.
  691. X *
  692. X * This is a preliminary version.  The final release will be offered
  693. X * with fewer restrictions.
  694. X *
  695. X * Nag should be launched out of your .login or .profile.  It
  696. X * periodically reads your ~/.nag file and executes commands
  697. X * that can be used as reminders of upcoming events.  The environment
  698. X * variable NAGFILE can be used to get input from something other than
  699. X * the ~/.nag file.
  700. X *
  701. X * NAGFILE FORMAT:
  702. X * ---------------
  703. X *
  704. X * The ~/.nag file should contain lines of the form:
  705. X *
  706. X *    status day time interval command
  707. X *
  708. X * where:
  709. X *
  710. X * status    is one of
  711. X *           1. '#' indicating a commented out reminder
  712. X *           2. ':' indicating a silenced reminder
  713. X *           3. ' ' for an activate reminder.
  714. X *        Other values produce unpredicatable results.
  715. X *
  716. X * day        is one of:
  717. X *           1.  A date, "8/8/88", "8-Aug-88", etc., but no blanks.
  718. X *           2.  '*' for any day.
  719. X *           3.  A day, "Sun", "Mon", ...
  720. X *        The last is presently unimplemented (sorry).
  721. X *
  722. X * time        is a time spec, "8AM", "23:00", etc., but no blanks
  723. X *
  724. X * interval    is a colon separated list of minutes after time at which
  725. X *           to execute the command, e.g.,
  726. X *
  727. X *             -30:-15:0:5:10
  728. X *
  729. X *           produces execution 30 and 15 minutes before the event,
  730. X *           at the time, and 5 and 10 minutes later.
  731. X *
  732. X * command    is a command to execute with /bin/sh.  Some shell variables
  733. X *        are set for use in messages:
  734. X *
  735. X *            $pretime    -interval
  736. X *            $posttime    interval
  737. X *            $now        hh:mm of the current time
  738. X *            $then        hh:mm of the parent event
  739. X *
  740. X * Blank lines are ignored.
  741. X *
  742. X * Example:
  743. X *
  744. X *    # don't forget to eat.
  745. X *     * 12:30PM 0 writebig "Lunch Time"
  746. X *
  747. X *    # Weekly warning that has been silenced.
  748. X *      :Mon 3:00PM -30:-20:-10:-5:0 echo "^GStatus report in $time"
  749. X *
  750. X *    # Active Weekly warning.
  751. X *     Fri 1:30PM -20:-10:-5:0:5:10 echo "^GCommittee meeting in $time"
  752. X *
  753. X *    # One shot warning to call the east coast.
  754. X *     8/25/86 1:30PM -180:-120:-60:0:10:20 echo "^GCall DEC Marlblerow"
  755. X *
  756. X * NAG
  757. X * ---
  758. X *
  759. X * Nag puts itself in the background, and exits when you logout.
  760. X * Standard output and standard error go to your terminal.
  761. X *
  762. X * Each time it wakes up, it sees if the ~/.nag file has changed.
  763. X * If so, it builds an event queue for lines without '#' comment symbols.
  764. X *
  765. X * Events that were not silenced with 'X' and were due before "now"
  766. X * are executed.  If the event was the last for an entry in ~/.nag,
  767. X * the file is edited to re-enable it from a gagged state.
  768. X *
  769. X * The program then sleeps for at most MINSLEEP minutes.
  770. X *
  771. X * OKOK
  772. X * ----
  773. X *
  774. X * The "okok" program just edits ~/.nag and prepends an 'X' to lines
  775. X * that need to be shut up.
  776. X *
  777. X * BUILD INSTRUCTIONS:
  778. X * -------------------
  779. X *
  780. X *  cc -o nag [ -DSYS5 ] nag.c gdate.c
  781. X *  ln nag okok
  782. X *
  783. X *  The code compiles for a BSD system by default.
  784. X *
  785. X * CAVEATS:
  786. X * --------
  787. X *
  788. X * Sorry Christopher, it probably won't work if stty nostop is set.
  789. X *
  790. X */
  791. X
  792. X# include <stdio.h>
  793. X# include <sys/types.h>
  794. X# include <sys/stat.h>
  795. X# include <signal.h>
  796. X# include <pwd.h>
  797. X# include <ctype.h>
  798. X
  799. X# ifdef SYS5
  800. X#    include        <string.h>
  801. X#    include        <time.h>
  802. X#    define        index        strchr
  803. X#    define        rindex        strrchr
  804. X# else
  805. X#    include        <strings.h>
  806. X#    include        <sys/time.h>
  807. X# endif
  808. X
  809. X/*----------------
  810. X *
  811. X *    defines
  812. X *
  813. X */
  814. X
  815. X# define DPRINTF    if(Debug) (void)fprintf
  816. X
  817. X# define COMCHAR    '#'
  818. X# define SILCHAR    ':'
  819. X
  820. X# define HRSECS        3600L
  821. X
  822. X# define CTIMELEN    32    /* length of a date/time string */
  823. X
  824. X# define MINSLEEP    (5*60)
  825. X# define MAXARGS    5120    /* max arg/env size on System V */
  826. X
  827. X# define TRUE        (1)
  828. X# define FALSE        (0)
  829. X
  830. X# define min(a,b)    ((a) < (b) ? (a) : (b))
  831. X
  832. X/*----------------
  833. X *
  834. X *    typedefs and structure definitions
  835. X *
  836. X */
  837. X
  838. X/*
  839. X * A NAGLINE is a parsed entry from the .nag file.  We keep
  840. X * a list of them representing the current file, so we can
  841. X * write it back out easily.
  842. X */
  843. X
  844. Xtypedef struct nagline    NAGLINE;
  845. X
  846. Xstruct nagline
  847. X{
  848. X    NAGLINE * next;        /* Next in the chain */
  849. X    int type;            /* COMMENT, SILENT, PENDING, BAD */
  850. X#    define UNKNOWN        0
  851. X#    define COMMENT        1
  852. X#    define SILENT        2
  853. X#    define PENDING        3
  854. X#    define BAD        4
  855. X
  856. X    int errtype;        /* if type is BAD, cause of error */
  857. X#    define NOERR        0
  858. X#    define EMPTY        1
  859. X#    define DATEBAD        2
  860. X#    define NOTIME        3
  861. X#    define TIMEBAD        4
  862. X#    define NOINTERVALS    5
  863. X#    define NOCMD        6
  864. X
  865. X    time_t atime;        /* absolute time of event */
  866. X    char *err;            /* string that caused the error */
  867. X    char *line;            /* the raw line, allocated */
  868. X    char *datestr;        /* the date string, allocated */
  869. X    char *timestr;        /* the time string, allocated */
  870. X    char *intstr;        /* extracted string of intervals, allocated */
  871. X    char *cmd;            /* extracted command to execute, allocated */
  872. X};
  873. X
  874. Xstatic
  875. Xchar *linetypes[] =
  876. X{
  877. X  "Unknown",
  878. X  "Comment",
  879. X  "Silent",
  880. X  "Pending",
  881. X  "Bad"
  882. X};
  883. X
  884. Xstatic
  885. Xchar *parserrs[] =
  886. X{
  887. X  "No error",
  888. X  "Empty line",
  889. X  "Bad date",
  890. X  "No time",
  891. X  "Bad time",
  892. X  "No intervals",
  893. X  "No command"
  894. X};
  895. X
  896. X
  897. X/*
  898. X * An EVENT is an entry in the event queue.
  899. X */
  900. X
  901. Xtypedef struct event    EVENT;
  902. X
  903. Xstruct event
  904. X{
  905. X    EVENT * next;        /* next event in chain */
  906. X    NAGLINE *lp;        /* the parent nagline */
  907. X    time_t etime;        /* absolute time of the event */
  908. X    int offset;            /* minutes difference with parent time */
  909. X};
  910. X
  911. X
  912. X/*----------------
  913. X *
  914. X *    File local variables
  915. X *
  916. X */
  917. X
  918. Xstatic char *Myname="";        /* name from argv[0] */
  919. Xstatic time_t Now = 0;        /* absolute time of "now" */
  920. Xstatic time_t Last = 0;        /* time last time we were awake */
  921. Xstatic NAGLINE *Flist = NULL;    /* lines from the file */
  922. Xstatic NAGLINE *Flast = NULL;    /* last line from the file */
  923. Xstatic EVENT *Evq = NULL;    /* the global event queue */
  924. Xstatic char Origlogin[20] = "";    /* login name when program started */
  925. Xstatic char Nagfile[ 256 ] = ""; /* full path of the nag file */
  926. Xstatic int Debug = FALSE;    /* debugging? */
  927. X
  928. Xstatic char Laststr[ CTIMELEN ]; /* ctime output for last time through */
  929. Xstatic char Nowstr[ CTIMELEN ];    /* ctime output for this time through */
  930. X
  931. X/*----------------
  932. X *
  933. X *    Forward and external function definitions
  934. X *
  935. X */
  936. X
  937. X/* library defined */
  938. X
  939. Xextern char *getlogin();    /* login name in /etc/utmp */
  940. Xextern char *getenv();        /* get an environment variable */
  941. Xextern struct passwd *getpwuid(); /* passwd entry for this user */
  942. Xextern time_t time();
  943. Xextern struct tm *localtime();
  944. Xextern char *fgets();
  945. Xextern char *index();
  946. Xextern char *rindex();
  947. Xextern int sprintf();
  948. Xextern void perror();        /* int on BSD? */
  949. Xextern void qsort();        /* int on BSD? */
  950. Xextern unsigned sleep();
  951. Xextern void free();
  952. Xextern void exit();
  953. Xextern char *ctime();
  954. X
  955. X/* gdate.c defined */
  956. X
  957. Xextern char *gdate();        /* date string to time buf struct */
  958. Xextern char *gtime();        /* time string to time buf struct */
  959. Xextern time_t tm_to_time();    /* time buf to secs past epoch    */
  960. Xextern char *dow[];        /* days of the week names */
  961. Xextern int find();        /* unambiguous search of string tables */
  962. X
  963. X/* forward function references */
  964. X
  965. X# define forward    extern
  966. X
  967. Xforward void nagfile();
  968. Xforward void setup();
  969. X
  970. Xforward int readf();
  971. Xforward int editf();
  972. Xforward int writef();
  973. X
  974. Xforward int parseline();
  975. Xforward void zaplines();
  976. X
  977. Xforward void buildq();
  978. Xforward void zapq();
  979. Xforward void insq();
  980. Xforward void addevents();
  981. Xforward int timecmp();
  982. Xforward void sortq();
  983. Xforward void runq();
  984. X
  985. Xforward void showlines();
  986. Xforward void dumpline();
  987. X
  988. Xforward void showevents();
  989. Xforward void dumpevent();
  990. X
  991. Xforward char *emalloc();
  992. Xforward char *ecalloc();
  993. Xforward FILE *efopen();
  994. X
  995. Xforward char *nctime();
  996. Xforward char *nhour();
  997. Xforward void delay();
  998. Xforward void lowcase();
  999. X
  1000. X/*----------------
  1001. X *
  1002. X * main() -- Main program.
  1003. X *
  1004. X * Do one time setup, then go into a loop rebuilding the event queue,
  1005. X * executing events in order.  Sleep is done after running the queue.
  1006. X *
  1007. X */
  1008. X/*ARGSUSED*/
  1009. Xmain(argc, argv)
  1010. Xint argc;
  1011. Xchar **argv;
  1012. X{
  1013. X    char *cp;
  1014. X
  1015. X    if(argc > 1)
  1016. X    Debug = TRUE;
  1017. X
  1018. X    Myname = (cp = rindex(argv[0], '/')) ? cp + 1 : argv[0] ;
  1019. X    nagfile();
  1020. X
  1021. X    if( !strcmp(Myname, "nag") )
  1022. X    {
  1023. X    setup();
  1024. X
  1025. X# ifndef FOREGROUND
  1026. X    DPRINTF(stderr, "forking to self-backgrounnd");
  1027. X    if(fork())
  1028. X        exit(0);
  1029. X# endif
  1030. X    /* pretend we started at the epoch */
  1031. X    Now = 0;
  1032. X    (void) strcpy( Nowstr, nctime( &Now ));
  1033. X
  1034. X    /*
  1035. X     * This loop never exits.
  1036. X     *
  1037. X     * The program terminates in delay() when the user logs
  1038. X     * off this terminal.
  1039. X     */
  1040. X    for(;;)
  1041. X    {
  1042. X        (void) strcpy( Laststr, Nowstr );
  1043. X        Last = Now;
  1044. X
  1045. X        Now = time(NULL);
  1046. X        (void) strcpy( Nowstr, nctime( &Now ) );
  1047. X
  1048. X        DPRINTF(stderr, "\nLoop:\tLast %s\tNow %s\n", Laststr, Nowstr);
  1049. X
  1050. X        if ( readf() )
  1051. X        buildq();
  1052. X
  1053. X        runq();
  1054. X    }
  1055. X    }
  1056. X    else if ( !strcmp(Myname, "okok"))
  1057. X    {
  1058. X    Now = time( NULL );
  1059. X    (void) strcpy( Nowstr, nctime( &Now ));
  1060. X
  1061. X    if ( readf() )
  1062. X    {
  1063. X        buildq();
  1064. X        if ( editf( PENDING ) )
  1065. X        exit( writef() );
  1066. X    }
  1067. X    else
  1068. X    {
  1069. X        (void) fprintf(stderr, "%s: Can't read %s\n", Myname, Nagfile );
  1070. X        exit(1);
  1071. X    }
  1072. X    }
  1073. X    else
  1074. X    {
  1075. X    (void) fprintf(stderr, "Identity crisis: \"%s\" bad program name\n",
  1076. X               argv[0]);
  1077. X    exit(1);
  1078. X    }
  1079. X    exit(0);
  1080. X    /*NOTREACHED*/
  1081. X}
  1082. X
  1083. X/*----------------
  1084. X *
  1085. X * nagfile -- get the full .nag file path
  1086. X *
  1087. X */
  1088. Xvoid
  1089. Xnagfile()
  1090. X{
  1091. X    register char *home;
  1092. X    register char *cp;
  1093. X
  1094. X    /* remember who you are to check for logout later */
  1095. X
  1096. X    (void) strcpy(Origlogin, getlogin());
  1097. X
  1098. X    /* expand the Nagfile name */
  1099. X
  1100. X    if( cp = getenv("NAGFILE") )
  1101. X    (void)strcpy( Nagfile, cp );
  1102. X    else if( home = getenv("HOME") )
  1103. X    (void) sprintf( Nagfile, "%s/.nag", home );
  1104. X    else
  1105. X    {
  1106. X    (void) fprintf(stderr, "%s: HOME is not set\n", Myname );
  1107. X    exit(1);
  1108. X    }
  1109. X
  1110. X    DPRINTF(stderr, "Origlogin %s, Nagfile %s\n", Origlogin, Nagfile);
  1111. X}
  1112. X
  1113. X/*----------------
  1114. X *
  1115. X * setup() -- one time initialization.
  1116. X *
  1117. X * Setup signals so we don't go away.
  1118. X * accidentally.
  1119. X *
  1120. X */
  1121. Xvoid
  1122. Xsetup()
  1123. X{
  1124. X    if(!Debug)
  1125. X    {
  1126. X    (void) signal( SIGQUIT, SIG_IGN );
  1127. X    (void) signal( SIGTERM, SIG_IGN );
  1128. X# ifdef SIGTTOU
  1129. X    (void) signal( SIGTTOU, SIG_IGN );
  1130. X# endif
  1131. X    }
  1132. X}
  1133. X
  1134. X
  1135. X/*----------------
  1136. X *
  1137. X * readf() -- read the nagfile and build in memory copy.
  1138. X *
  1139. X * Returns TRUE if the file was read.
  1140. X */
  1141. Xint
  1142. Xreadf()
  1143. X{
  1144. X    register NAGLINE *lp;
  1145. X    register FILE *fp;
  1146. X    char line[ MAXARGS ];
  1147. X    struct stat newstat;
  1148. X    static struct stat laststat = { 0 };
  1149. X    static time_t readtime = 0;
  1150. X
  1151. X    /* check to see if Nagfile has changed, and reread file. */
  1152. X
  1153. X    if(stat(Nagfile, &newstat))
  1154. X    {
  1155. X    /* set it the epoch, but don't complain */
  1156. X    newstat.st_mtime = 0;
  1157. X    }
  1158. X
  1159. X    /* if file changed, or we read it more than 12 hours ago */
  1160. X
  1161. X    if ( newstat.st_mtime <= laststat.st_mtime
  1162. X    || (readtime && Now > 0 && readtime < (Now - (HRSECS * 12))))
  1163. X    {
  1164. X    DPRINTF(stderr, "already read %s\n", Nagfile );
  1165. X    return FALSE;
  1166. X    }
  1167. X
  1168. X    /* rebuild the internal copy of the file */
  1169. X
  1170. X    DPRINTF(stderr, "reading Nagfile\n");
  1171. X
  1172. X    laststat = newstat;
  1173. X    readtime = Now;
  1174. X
  1175. X    zaplines();
  1176. X
  1177. X    /* warn, but don't fatal if file can't be opened this time through */
  1178. X
  1179. X    if ( NULL==(fp = efopen(Nagfile, "r")))
  1180. X    return FALSE;
  1181. X
  1182. X    /* build the new incore copy */
  1183. X
  1184. X    while( NULL != fgets( line, sizeof(line), fp ) )
  1185. X    {
  1186. X    /* Lose trailing newline */
  1187. X    line[ strlen(line) - 1 ] = '\0';
  1188. X
  1189. X    /*ALIGNOK*/
  1190. X    lp = (NAGLINE *) ecalloc( sizeof(*lp), 1 );
  1191. X
  1192. X    if( parseline( line, lp ) )
  1193. X    {
  1194. X        if( lp->type == BAD )
  1195. X        DPRINTF(stderr, "Parsed OK: %s\n", lp->line );
  1196. X        else
  1197. X        DPRINTF(stderr, "Parsed OK: %s %s %s %s\n",
  1198. X            lp->datestr,
  1199. X            lp->timestr,
  1200. X            lp->intstr,
  1201. X            lp->cmd );
  1202. X    }
  1203. X    else
  1204. X    {
  1205. X        (void) fprintf(stderr, "%s: Can't parse line:\n%s\n%s %s\n",
  1206. X               Myname,
  1207. X               lp->line,
  1208. X               parserrs[ lp->errtype ],
  1209. X               lp->err );
  1210. X    }
  1211. X
  1212. X    if( !Flist )
  1213. X        Flist = lp;
  1214. X    if( Flast )
  1215. X        Flast->next = lp;
  1216. X    Flast = lp;
  1217. X    }
  1218. X    (void) fclose(fp);
  1219. X
  1220. X    if(Debug)
  1221. X    {
  1222. X    (void) fprintf(stderr, "Read file OK\n");
  1223. X    showlines( "\nLines after file read in:\n" );
  1224. X    }
  1225. X
  1226. X    return TRUE;
  1227. X}
  1228. X
  1229. X
  1230. X/*----------------
  1231. X *
  1232. X * editf() -- interactively edit the nag file in memory, then write it out.
  1233. X *
  1234. X * Used by 'okok' to make PENDING events SILENT; can also be used to
  1235. X * make SILENT events PENDING.
  1236. X *
  1237. X * Goes WAY out of it's way to force i/o to be on the terminal.
  1238. X *
  1239. X * Returns TRUE if lines were changed.
  1240. X */
  1241. Xint
  1242. Xeditf( what )
  1243. Xregister int what;
  1244. X{
  1245. X    register FILE *ifp;
  1246. X    register FILE *ofp;
  1247. X    register NAGLINE *lp;
  1248. X    register EVENT *ep;
  1249. X    register int changed = FALSE;
  1250. X
  1251. X    char buf[ 80 ];
  1252. X
  1253. X    if( ( ifp = efopen( "/dev/tty", "r" ) ) == NULL )
  1254. X    return( changed );
  1255. X
  1256. X    if( ( ofp = efopen( "/dev/tty", "w" ) ) == NULL )
  1257. X    return( changed );
  1258. X
  1259. X    setbuf( ofp, NULL );    /* force output to be unbuffered */
  1260. X
  1261. X    for( lp = Flist; lp ; lp = lp->next )
  1262. X    {
  1263. X    if( lp->type == what )
  1264. X    {
  1265. X        /* only display events on the queue within 12 hours */
  1266. X
  1267. X        for( ep = Evq; ep && ep->lp != lp; ep = ep->next )
  1268. X        continue;
  1269. X
  1270. X        if( !ep || ep->etime > Now + (HRSECS * 12) )
  1271. X        continue;
  1272. X
  1273. X        (void) fprintf( ofp, "Silence %s: %s (y/n/q)? ",
  1274. X                    lp->timestr, lp->cmd ) ;
  1275. X
  1276. X        if( fgets( buf, sizeof(buf), ifp ) == NULL )
  1277. X        break;
  1278. X
  1279. X        if( buf[ 0 ] == 'y' || buf[ 0 ] == 'Y' )
  1280. X        {
  1281. X        lp->type = ( what == PENDING ) ? SILENT : PENDING;
  1282. X        changed = TRUE;
  1283. X        }
  1284. X
  1285. X        /* stop querying if a 'q' is entered */
  1286. X
  1287. X        if( buf[ 0 ] == 'q' || buf[ 0 ] == 'Q' )
  1288. X            break;
  1289. X    }
  1290. X    }
  1291. X    (void) fclose( ifp );
  1292. X    (void) fclose( ofp );
  1293. X    return ( changed );
  1294. X}
  1295. X
  1296. X
  1297. X/*----------------
  1298. X *
  1299. X * writef() -- Write the file back out after a change.
  1300. X *
  1301. X * Returns TRUE if file wrote OK.
  1302. X */
  1303. Xint
  1304. Xwritef()
  1305. X{
  1306. X    char buf[ 80 ];
  1307. X
  1308. X    register int err;
  1309. X    register FILE *fp;
  1310. X    register NAGLINE *lp;
  1311. X
  1312. X    DPRINTF(stderr, "Writing %s\n", Nagfile );
  1313. X
  1314. X    if( ( fp = efopen( Nagfile, "w" ) ) == NULL )
  1315. X    return (FALSE);
  1316. X
  1317. X    err = 0;
  1318. X    for( lp = Flist; lp && err >= 0 ; lp = lp->next )
  1319. X    {
  1320. X    switch( lp->type )
  1321. X    {
  1322. X    case BAD:
  1323. X    case COMMENT:
  1324. X        err = fprintf( fp, "%s\n", lp->line );
  1325. X        break;
  1326. X    default:
  1327. X        err = fprintf( fp, "%c%s %s %s %s\n",
  1328. X              lp->type == SILENT ? SILCHAR : ' ',
  1329. X              lp->datestr,
  1330. X              lp->timestr,
  1331. X              lp->intstr,
  1332. X              lp->cmd );
  1333. X        break;
  1334. X    }
  1335. X    }
  1336. X
  1337. X    if( err < 0 )
  1338. X    {
  1339. X    DPRINTF( stderr, "err %d\n", err );
  1340. X    (void) sprintf( buf, "%s: error writing %s", Myname, Nagfile );
  1341. X    perror( buf );
  1342. X    }
  1343. X    else if( (err = fclose( fp ) ) < 0 )
  1344. X    {
  1345. X    (void) sprintf( buf, "%s: error closing %s", Myname, Nagfile );
  1346. X    perror( buf );
  1347. X    return( FALSE );
  1348. X    }
  1349. X    return ( err >= 0 );
  1350. X}
  1351. X
  1352. X
  1353. X/*----------------
  1354. X *
  1355. X * parseline() -- Split text into a NAGLINE more amenable to processing.
  1356. X *
  1357. X *    Returns TRUE with the NAGLINE all set up if parsed OK.
  1358. X *    Returns FALSE with the line->type set to BAD,
  1359. X *              and line->errtype set if undecipherable.
  1360. X *
  1361. X *
  1362. X *    in the code, buf points to the first character not processed,
  1363. X *                   cp points to the last character examined.
  1364. X *
  1365. X *               cp places nulls in likely places.
  1366. X *
  1367. X *    This is a very ugly function and should be rewritten.
  1368. X */
  1369. Xint
  1370. Xparseline( buf, lp )
  1371. Xregister char *buf;
  1372. Xregister NAGLINE *lp;
  1373. X{
  1374. X    register char *cp;
  1375. X    register int  today;
  1376. X    register int    i;
  1377. X    time_t    d;
  1378. X    time_t    t;
  1379. X    int        anyday;    
  1380. X    struct tm ntm;        /* now tm struct */
  1381. X    struct tm dtm;        /* date tm struct */
  1382. X    struct tm ttm;        /* time tm struct */
  1383. X
  1384. X    anyday = FALSE;
  1385. X    lp->line = strcpy( emalloc( strlen( buf ) + 1 ), buf );
  1386. X
  1387. X    /*
  1388. X     * determine line type, and advance buf to first non-blank after
  1389. X     * the status field
  1390. X     */
  1391. X
  1392. X    switch (*buf)
  1393. X    {
  1394. X    case COMCHAR:
  1395. X    lp->type = COMMENT;
  1396. X    return TRUE;
  1397. X    /*NOTREACHED*/
  1398. X
  1399. X    case SILCHAR:
  1400. X    lp->type = SILENT;
  1401. X    buf++;
  1402. X    break;
  1403. X
  1404. X    default:
  1405. X    lp->type = PENDING;
  1406. X    break;
  1407. X    }
  1408. X
  1409. X    /* skip to non-whitespace */
  1410. X
  1411. X    while( *buf && isspace(*buf))
  1412. X    buf++;
  1413. X
  1414. X    /* empty line isn't fatal (it's a comment) */
  1415. X
  1416. X    if (!*buf) {
  1417. X    lp->type = BAD;
  1418. X    lp->errtype = EMPTY;
  1419. X    lp->err = buf;
  1420. X    return TRUE;
  1421. X    }
  1422. X
  1423. X    /* bracket the day/date, and null terminate it */
  1424. X
  1425. X    for( cp = buf; *cp && !isspace( *cp ); cp++ )
  1426. X    continue;
  1427. X    if( *cp ) *cp++ = '\0';
  1428. X    else *cp = '\0';
  1429. X
  1430. X    /* cp now positioned at char past null, or on null at the end */
  1431. X
  1432. X    /*
  1433. X     * buf points at the day field; figure out the
  1434. X     * absolute time of "Midnight" of the right day for the event.
  1435. X     */
  1436. X    lp->datestr = strcpy( emalloc( strlen( buf ) + 1 ), buf );
  1437. X
  1438. X    /* figure when midnight of today was */
  1439. X
  1440. X    ntm = *localtime( &Now );
  1441. X    ntm.tm_sec = 0;
  1442. X    ntm.tm_min = 0;
  1443. X    ntm.tm_hour = 0;
  1444. X
  1445. X    if (*buf == '*')
  1446. X    {
  1447. X    anyday = TRUE;
  1448. X    dtm = ntm;
  1449. X    }
  1450. X    else
  1451. X    {
  1452. X
  1453. X    /* parse date */
  1454. X
  1455. X    if( NULL != gdate( buf, &dtm ) )
  1456. X    {
  1457. X        DPRINTF(stderr, "not a date, maybe a day\n");
  1458. X
  1459. X        /* maybe it's a day name... */
  1460. X
  1461. X        lowcase( buf );
  1462. X        if( (i = find( buf, dow )) >= 0 )
  1463. X        {
  1464. X        i--;
  1465. X        today = ntm.tm_wday;
  1466. X        DPRINTF(stderr, "today %s, event %s\n",
  1467. X            dow[ today ],
  1468. X            dow[ i ] );
  1469. X        if( i < today )
  1470. X            i += 7;    /* it's next week */
  1471. X        d = Now + (( i - today ) * HRSECS * 24 );
  1472. X        dtm = *localtime( &d );
  1473. X        dtm.tm_sec = 0;
  1474. X        dtm.tm_min = 0;
  1475. X        dtm.tm_hour = 0;
  1476. X        }
  1477. X        else
  1478. X        {
  1479. X        DPRINTF(stderr, "find of %s in dow returned %d\n", buf, i );
  1480. X        lp->type = BAD;
  1481. X        lp->errtype = DATEBAD;
  1482. X        lp->err = buf;
  1483. X        return FALSE;
  1484. X        }
  1485. X    }
  1486. X    }
  1487. X
  1488. X    d = tm_to_time( &dtm );
  1489. X    DPRINTF(stderr, "parseline: date %s\n", nctime(&d) );
  1490. X
  1491. X    /* advance to time */
  1492. X
  1493. X    for( buf = cp ; *buf && isspace(*buf); buf++)    /* skip blanks */
  1494. X    continue;
  1495. X
  1496. X    if (!*buf) {
  1497. X    lp->type = BAD;
  1498. X    lp->errtype = NOTIME;
  1499. X    lp->err = buf;
  1500. X    return FALSE;
  1501. X    }
  1502. X
  1503. X    /* bracket the time */
  1504. X
  1505. X    for( cp = buf; *cp && !isspace( *cp ); cp++ )
  1506. X    continue;
  1507. X    if( *cp ) *cp++ = '\0';
  1508. X    else *cp = '\0';
  1509. X
  1510. X    /*
  1511. X     * buf now at time field, figure offset until event,
  1512. X     * then fill in absolute time.
  1513. X     *
  1514. X     * gtime can't fail -- it will say it's 00:00 if it
  1515. X     * doesn't understand.
  1516. X     */
  1517. X    DPRINTF(stderr, "parseline: time buf %s\n", buf );
  1518. X    lp->timestr = strcpy( emalloc( strlen( buf ) + 1 ), buf );
  1519. X    (void) gtime( buf, &ttm );
  1520. X    t = (ttm.tm_hour * HRSECS) + (ttm.tm_min * 60);
  1521. X    lp->atime = d + t;
  1522. X
  1523. X    /*
  1524. X    ** If past the event, and it's for any day, do it tomorrow. 
  1525. X    ** BUG:  This breaks if there is an interval after the event
  1526. X    ** This is a rare case, and I haven't yet thought of a clean fix.
  1527. X    */
  1528. X    if( anyday && lp->atime < Now )
  1529. X        lp->atime += HRSECS * 24;
  1530. X
  1531. X    DPRINTF(stderr, "parseline: time offset %s is %d seconds, %02d:%02d\n",
  1532. X        buf, t, t / HRSECS, t % HRSECS );
  1533. X    DPRINTF(stderr, "parseline: etime %s\n", nctime(&lp->atime));
  1534. X
  1535. X    /* advance to intervals */
  1536. X
  1537. X    for( buf = cp; *buf && isspace(*buf); buf++)
  1538. X    continue;
  1539. X
  1540. X    if (!*buf)
  1541. X    {
  1542. X    lp->type = BAD;
  1543. X    lp->errtype = NOINTERVALS;
  1544. X    lp->err = buf;
  1545. X    return FALSE;
  1546. X    }
  1547. X
  1548. X    /* bracket the intervals */
  1549. X
  1550. X    for( cp = buf; *cp && !isspace( *cp ); cp++ )
  1551. X    continue;
  1552. X    if( *cp ) *cp++ = '\0';
  1553. X    else *cp = '\0';
  1554. X
  1555. X    /* save the interval string. */
  1556. X
  1557. X    lp->intstr = strcpy( emalloc( strlen( buf ) + 1 ), buf );
  1558. X
  1559. X    /* take rest of the line as the command */
  1560. X
  1561. X    if (!*cp)
  1562. X    {
  1563. X    lp->type = BAD;
  1564. X    lp->errtype = NOCMD;
  1565. X    lp->err = strcpy( emalloc ( strlen( cp ) + 1 ), cp );
  1566. X    return FALSE;
  1567. X    }
  1568. X
  1569. X    lp->cmd = strcpy( emalloc ( strlen( cp ) + 1 ), cp );
  1570. X
  1571. X    return TRUE;
  1572. X}
  1573. X
  1574. X
  1575. X/*----------------
  1576. X *
  1577. X * zaplines() -- delete all NAGLINEs and free their space
  1578. X *
  1579. X */
  1580. Xvoid
  1581. Xzaplines()
  1582. X{
  1583. X    register NAGLINE *lp;
  1584. X    register NAGLINE *nlp;
  1585. X
  1586. X    for( lp = Flist; lp ; lp = nlp )
  1587. X    {
  1588. X    nlp = lp->next;
  1589. X
  1590. X    if( lp->line )
  1591. X        free(lp->line);
  1592. X    if( lp->datestr )
  1593. X        free(lp->datestr);
  1594. X    if( lp->timestr )
  1595. X        free(lp->timestr);
  1596. X    if( lp->intstr )
  1597. X        free(lp->intstr);
  1598. X    if( lp->cmd )
  1599. X        free(lp->cmd);
  1600. X
  1601. X    free( lp );
  1602. X    }
  1603. X    Flast = Flist = NULL;
  1604. X}
  1605. X
  1606. X
  1607. X/*----------------
  1608. X *
  1609. X * buildq() --  Rebuild the event queue if the .nag file has changed.
  1610. X *
  1611. X */
  1612. Xvoid
  1613. Xbuildq()
  1614. X{
  1615. X    register NAGLINE *lp;
  1616. X
  1617. X    DPRINTF(stderr, "buildq: rebuilding the event queue\n");
  1618. X
  1619. X    zapq();
  1620. X
  1621. X    for( lp = Flist; lp; lp = lp->next )
  1622. X    {
  1623. X    /* add events for silenced lines too. */
  1624. X    if( lp->type != COMMENT )
  1625. X        addevents( lp );
  1626. X    }
  1627. X
  1628. X    sortq();
  1629. X
  1630. X    if(Debug)
  1631. X    showevents( "Event queue after rebuild and sort\n" );
  1632. X}
  1633. X
  1634. X
  1635. X/*----------------
  1636. X *
  1637. X * zapq() -- Destroy an event queue, setting the head back to NULL.
  1638. X *
  1639. X * Only the actual element is freed.
  1640. X */
  1641. Xvoid
  1642. Xzapq()
  1643. X{
  1644. X    register EVENT *this;
  1645. X    register EVENT *next;
  1646. X
  1647. X    for ( this = Evq; this ; this = next )
  1648. X    {
  1649. X    next = this->next;
  1650. X    free( this );
  1651. X    }
  1652. X    Evq = NULL;
  1653. X}
  1654. X
  1655. X/*----------------
  1656. X *
  1657. X * insq() -- Add a new EVENT to the head of a queue.
  1658. X *
  1659. X */
  1660. Xvoid
  1661. Xinsq( etime, offset, lp )
  1662. Xtime_t etime;
  1663. Xregister int offset;
  1664. XNAGLINE *lp;
  1665. X{
  1666. X    register EVENT *ep;
  1667. X
  1668. X    etime += (offset * 60);
  1669. X
  1670. X    /* add events after last time we ran, but no more than 24 hours
  1671. X       in the future */
  1672. X
  1673. X    if( ( etime >= Now || ( Last && etime > Last ) )
  1674. X       && etime < ( Now + ( HRSECS * 24 ) ) )
  1675. X    {
  1676. X    DPRINTF(stderr, "insq: Adding %s at %s\n", lp->cmd, nctime(&etime) );
  1677. X    }
  1678. X    else            /* too late */
  1679. X    {
  1680. X    DPRINTF(stderr, "insq: Dropping %s at %s\n", lp->cmd, nctime(&etime) );
  1681. X    return;
  1682. X    }
  1683. X
  1684. X    /*ALIGNOK*/
  1685. X    ep = (EVENT *) emalloc( sizeof(*ep) );
  1686. X    ep->etime = etime;
  1687. X    ep->offset = offset;
  1688. X    ep->lp = lp;
  1689. X
  1690. X    /* splice into the head of the queue */
  1691. X    ep->next = Evq;        /* NULL, if last event */
  1692. X    Evq = ep;
  1693. X}
  1694. X
  1695. X
  1696. X/*----------------
  1697. X *
  1698. X * addevents() -- Add pending events for the NAGLINE to the queue.
  1699. X *
  1700. X * Events in the past are not considered.
  1701. X * If the command has been silenced, don't do the command.
  1702. X *
  1703. X */
  1704. Xvoid
  1705. Xaddevents( lp )
  1706. Xregister NAGLINE *lp;
  1707. X{
  1708. X    register char *cp;        /* ptr into the interval string */
  1709. X    int offset;            /* offset in minutes */
  1710. X
  1711. X    /* for every numeric value in the interval string... */
  1712. X
  1713. X    for( cp = lp->intstr; cp && *cp ; cp = index( cp, ':' ) )
  1714. X    {
  1715. X    if (*cp == ':')        /* skip past optional ':' */
  1716. X        cp++;
  1717. X    if (!*cp)        /* ignore trailing ':' */
  1718. X        return;
  1719. X
  1720. X    /* read (possibly) signed interval value */
  1721. X
  1722. X    if( 1 != sscanf( cp, "%d", &offset ) )
  1723. X    {
  1724. X        (void) fprintf(stderr, "%s: bad intervals '%s'\n", Myname,
  1725. X               lp->intstr );
  1726. X        return;
  1727. X    }
  1728. X    insq( lp->atime, offset, lp );
  1729. X    }
  1730. X}
  1731. X
  1732. X
  1733. X
  1734. X/*----------------
  1735. X *
  1736. X * timecmp() -- Compare time of two events.
  1737. X *
  1738. X * Made slightly tricky since it must return an int, not a time_t.
  1739. X *
  1740. X */
  1741. Xint
  1742. Xtimecmp( a, b )
  1743. Xregister EVENT **a;
  1744. Xregister EVENT **b;
  1745. X{
  1746. X    time_t val = (*a)->etime - (*b)->etime;
  1747. X
  1748. X    return( val < 0 ? -1 : val > 0 );
  1749. X}
  1750. X
  1751. X
  1752. X/*----------------
  1753. X *
  1754. X * sortq() -- Sort the event queue into chronological order.
  1755. X *
  1756. X * 1. Create an array of pointers to the events in the queue.
  1757. X * 2. Sort the array by time of the pointed-to events.
  1758. X * 3. Rebuild the queue in the order of the array.
  1759. X *
  1760. X */
  1761. Xvoid
  1762. Xsortq()
  1763. X{
  1764. X    register unsigned int n;    /* number of events in the queue */
  1765. X    register unsigned int i;    /* handy counter */
  1766. X    register EVENT **events;    /* allocated array of EVENT ptrs */
  1767. X    register EVENT **ap;    /* ptr into allocated events */
  1768. X    register EVENT *ep;        /* pointer in event chain */
  1769. X
  1770. X    forward int timecmp();
  1771. X
  1772. X    n = 0;
  1773. X    for( ep = Evq; ep; ep = ep->next )
  1774. X    n++;
  1775. X
  1776. X    DPRINTF(stderr, "sortq:  %d events\n", n );
  1777. X
  1778. X    if ( n < 2 )
  1779. X    return;
  1780. X
  1781. X    /* build array of ptrs to events */
  1782. X
  1783. X    /*ALIGNOK*/
  1784. X    ap = events = (EVENT **) ecalloc( (unsigned)sizeof(**ap), n );
  1785. X
  1786. X    /* build array of ptrs to events */
  1787. X    for( ep = Evq; ep; ep = ep->next )
  1788. X    *ap++ = ep;
  1789. X
  1790. X    /* sort by ascending time */
  1791. X    (void) qsort( events, (unsigned)n, sizeof(*events), timecmp );
  1792. X
  1793. X    /* rechain the event queue from the sorted array */
  1794. X    Evq = ep = events[0];
  1795. X    for ( i = 0 ; i < n ; )
  1796. X    {
  1797. X    ep->next = events[i++];
  1798. X    ep = ep->next;
  1799. X    }
  1800. X    ep->next = NULL;
  1801. X
  1802. X    free( events );
  1803. X}
  1804. X
  1805. X
  1806. X/*----------------
  1807. X *
  1808. X * runq() -- Execute all events that are due.
  1809. X *
  1810. X * Sleep until the next scheduled event.  If there are none, or
  1811. X * next is far away, sleep for MINSLEEP and try again.
  1812. X *
  1813. X */
  1814. Xvoid
  1815. Xrunq()
  1816. X{
  1817. X    char cmd[ 5120 ];
  1818. X    char now[ CTIMELEN ];
  1819. X    register EVENT *evq;    /* standin for global Evq in loop */
  1820. X    register EVENT *ep;        /* next event */
  1821. X    register NAGLINE *lp;
  1822. X    int dsecs;
  1823. X
  1824. X    DPRINTF(stderr, "runq start at %s\n", Nowstr );
  1825. X
  1826. X    evq = Evq;            /* fast access, be sure to save back */
  1827. X
  1828. X    /*
  1829. X     * Execute commands that are due.
  1830. X     *
  1831. X     * Keeps head of the queue current by cutting out events as
  1832. X     * they are processed.
  1833. X     *
  1834. X     * The loop breaks out when the queue is gobbled up,
  1835. X     * or we get to an event that is not due now.
  1836. X     */
  1837. X
  1838. X    while( evq && evq->etime <= Now )
  1839. X    {
  1840. X    lp = evq->lp;
  1841. X
  1842. X    DPRINTF(stderr, "due at %s:\n", nctime( &evq->etime ) );
  1843. X
  1844. X    /* Run a PENDING event */
  1845. X
  1846. X    if( lp->type == PENDING && lp->cmd )
  1847. X    {
  1848. X        (void)strcpy( now, &Nowstr[ 11 ] );
  1849. X        now[ 5 ] = '\0';
  1850. X
  1851. X        (void)sprintf( cmd, "pretime=%d;posttime=%d;now=%s;then=%s;%s\n",
  1852. X              -evq->offset,
  1853. X              evq->offset,
  1854. X              now,
  1855. X              nhour( &lp->atime ),
  1856. X              lp->cmd );
  1857. X
  1858. X        DPRINTF(stderr, "executing:\n%s\n", cmd );
  1859. X        if( system( cmd ) )
  1860. X        (void) fprintf( stderr, "%s: Trouble running\n'%s'\n",
  1861. X                   Myname, cmd );
  1862. X    }
  1863. X
  1864. X    /* if it's a SILENT event, is it time to make it PENDING? */
  1865. X
  1866. X    if( lp->type == SILENT )
  1867. X    {
  1868. X        /* find the queue end or the next event for the line */
  1869. X
  1870. X        for( ep = evq->next ; ep && ep->lp != lp ; ep = ep->next )
  1871. X        continue;
  1872. X
  1873. X        /* if match, or it was the last in the queue, turn it on */
  1874. X
  1875. X        if ( ep )
  1876. X        {
  1877. X        DPRINTF(stderr, "SILENT event\n");
  1878. X        }
  1879. X        else
  1880. X        {
  1881. X        DPRINTF(stderr, "Last SILENT event, making PENDING again.\n");
  1882. X        lp->type = PENDING;
  1883. X
  1884. X        /*
  1885. X         * if the write fails, keep going and hope the user fixes
  1886. X         * the nag file.  If we exit, the daemon would need
  1887. X         * to be restarted by hand.  Since it won't do anything
  1888. X         * but sleep and exit when the user logs off, no harm
  1889. X         * is done by sticking around.
  1890. X         */
  1891. X        (void) writef();
  1892. X        }
  1893. X    }
  1894. X    ep = evq->next;
  1895. X    free( evq );
  1896. X    evq = ep;
  1897. X    }                /* for events on the queue */
  1898. X
  1899. X    dsecs = evq ? min( evq->etime - Now, MINSLEEP) : MINSLEEP;
  1900. X
  1901. X    DPRINTF(stderr, "sleeping for %d seconds, next %s\n",
  1902. X        dsecs,
  1903. X        evq ? nctime( &evq->etime ) : "never" );
  1904. X
  1905. X    Evq = evq;            /* back to global var */
  1906. X
  1907. X    delay( dsecs );
  1908. X}
  1909. X
  1910. X
  1911. X/*----------------
  1912. X *
  1913. X * emalloc() -- malloc with error msg.
  1914. X *
  1915. X */
  1916. Xchar *
  1917. Xemalloc( size )
  1918. Xregister int size;
  1919. X{
  1920. X    register char *ptr;
  1921. X    extern char *malloc();
  1922. X
  1923. X    if ( ( ptr = malloc( (unsigned) size ) ) == NULL )
  1924. X    {
  1925. X    (void) fprintf(stderr, "%s: Can't malloc %d bytes\n", Myname, size );
  1926. X    exit(1);
  1927. X    }
  1928. X    return( ptr );
  1929. X}
  1930. X
  1931. X/*----------------
  1932. X *
  1933. X * ecalloc() -- calloc with error message.
  1934. X *
  1935. X */
  1936. Xchar *
  1937. Xecalloc( n, size )
  1938. Xregister unsigned int n;
  1939. Xregister unsigned int size;
  1940. X{
  1941. X    register char *ptr;
  1942. X    extern char *calloc();
  1943. X
  1944. X    if ( ( ptr = calloc( (unsigned) size, n ) ) == NULL )
  1945. X    {
  1946. X    (void) fprintf(stderr, "%s: Can't calloc %d bytes\n", Myname, size * n);
  1947. X    exit(1);
  1948. X    }
  1949. X    return( ptr );
  1950. X}
  1951. X
  1952. X/*
  1953. X * efopen()  -- fopen with error message on failure (no fatal error)
  1954. X */
  1955. XFILE *
  1956. Xefopen( file, mode )
  1957. Xchar *file;
  1958. Xchar *mode;
  1959. X{
  1960. X    char buf [ 80 ];
  1961. X    register FILE * fp;
  1962. X
  1963. X    if( (fp = fopen( file, mode )) == NULL )
  1964. X    {
  1965. X    (void)sprintf( buf, "%s: can't open file %s with mode \"%s\"",
  1966. X              Myname, file, mode );
  1967. X    perror( buf );
  1968. X    }
  1969. X    return( fp );
  1970. X}
  1971. X
  1972. X
  1973. X/*
  1974. X * showline() -- Dump the line list.
  1975. X */
  1976. Xvoid
  1977. Xshowlines( msg )
  1978. Xchar *msg;
  1979. X{
  1980. X    register NAGLINE *lp;
  1981. X
  1982. X    (void) fprintf(stderr, "%s", msg );
  1983. X    for( lp = Flist; lp ; lp = lp->next )
  1984. X    dumpline( lp );
  1985. X}
  1986. X
  1987. X/*
  1988. X * dumpline() -- dump a NAGLINE for debugging.
  1989. X */
  1990. Xvoid
  1991. Xdumpline( lp )
  1992. Xregister NAGLINE *lp;
  1993. X{
  1994. X    if( lp == NULL )
  1995. X    {
  1996. X    (void) fprintf(stderr, "dumpline: NULL lp\n");
  1997. X    return;
  1998. X    }
  1999. X    (void) fprintf(stderr, "\nline (%s):\n%s\n", linetypes[ lp->type ],
  2000. X           lp->line );
  2001. X    switch( lp->type )
  2002. X    {
  2003. X    case BAD:
  2004. X    (void) fprintf(stderr, "%s %s\n", parserrs[ lp->errtype ], lp->err );
  2005. X    break;
  2006. X
  2007. X    case PENDING:
  2008. X    case SILENT:
  2009. X    (void) fprintf(stderr, "The event is at %s\n", nctime( &lp->atime ));
  2010. X    }
  2011. X}
  2012. X
  2013. X/*
  2014. X * showevents() -- dump the event list, for debugging.
  2015. X */
  2016. Xvoid
  2017. Xshowevents( msg )
  2018. Xchar *msg;
  2019. X{
  2020. X    register EVENT *ep;
  2021. X
  2022. X    (void) fprintf(stderr, "%s", msg );
  2023. X    for( ep = Evq; ep; ep = ep->next )
  2024. X    dumpevent( ep );
  2025. X}
  2026. X
  2027. X/*
  2028. X * dumpevent() -- print an event, for debugging.
  2029. X */
  2030. Xvoid
  2031. Xdumpevent( ep )
  2032. Xregister EVENT *ep;
  2033. X{
  2034. X    if( ep == NULL )
  2035. X    (void) fprintf(stderr, "dumpevent: NULL ep\n");
  2036. X    else
  2037. X    (void) fprintf(stderr, "event 0x%x, next 0x%x offset %d time %s\n",
  2038. X               ep, ep->next, ep->offset, nctime(&ep->etime) );
  2039. X}
  2040. X
  2041. X/*
  2042. X * nctime() -- ctime with trailing '\n' whacked off.
  2043. X */
  2044. Xchar *
  2045. Xnctime( t )
  2046. Xtime_t *t;
  2047. X{
  2048. X    register char *cp;
  2049. X
  2050. X    cp = ctime( t );
  2051. X    cp[ strlen( cp ) - 1 ] = '\0';
  2052. X    return ( cp );
  2053. X}
  2054. X
  2055. X/*
  2056. X * nhour() -- return an hh:mm string given a pointer to a time_t.
  2057. X */
  2058. Xchar *
  2059. Xnhour( t )
  2060. Xtime_t *t;
  2061. X{
  2062. X    register char *buf = ctime( t );
  2063. X
  2064. X    /*
  2065. X     * 012345678901234567890123
  2066. X     * Wed Dec 31 16:00:00 1969
  2067. X     */
  2068. X
  2069. X    buf[ 16 ] = '\0';
  2070. X    return ( &buf[ 11 ] );
  2071. X}
  2072. X
  2073. X
  2074. X/*----------------
  2075. X *
  2076. X * delay() -- like sleep but knows what 0 means.
  2077. X *
  2078. X * If user logs out, notices and exit with OK status.
  2079. X *
  2080. X */
  2081. Xvoid
  2082. Xdelay( secs )
  2083. Xint secs;
  2084. X{
  2085. X    char thislogin[20];
  2086. X
  2087. X    if( secs > 0)
  2088. X    {
  2089. X    (void) sleep( (unsigned) secs );
  2090. X    (void) strcpy(thislogin, getlogin());
  2091. X    if ( strcmp(Origlogin, thislogin) )
  2092. X        exit(0);
  2093. X    }
  2094. X}
  2095. X
  2096. X/*
  2097. X * lowcase() -- make a string all lower case.
  2098. X */
  2099. Xvoid
  2100. Xlowcase( s )
  2101. Xchar *s;
  2102. X{
  2103. X    while ( *s )
  2104. X    {
  2105. X    if( isupper( *s ) )
  2106. X        *s = tolower( *s );
  2107. X    s++;
  2108. X    }
  2109. X}
  2110. X
  2111. X# if 0
  2112. X
  2113. X/*
  2114. X * dumptm() -- show contents of a tm structure.
  2115. X */
  2116. Xdumptm( tm )
  2117. Xstruct tm *tm;
  2118. X{
  2119. X    (void) fprintf(stderr, "year : %d month: %d day: %d\n",
  2120. X           tm->tm_year,tm->tm_mon,tm->tm_mday);
  2121. X    (void) fprintf(stderr, "day of month: %d hour: %d minute: %d second: %d\n",
  2122. X           tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec) ;
  2123. X    (void) fprintf(stderr, "day of year: %d day of week: %d dst: %d\n",
  2124. X           tm->tm_yday, tm->tm_wday, tm->tm_isdst) ;
  2125. X}
  2126. X
  2127. X# endif
  2128. X
  2129. X/* end of nag.c */
  2130. X
  2131. X
  2132. SHAR_EOF
  2133. if test 29846 -ne "`wc -c 'nag.c'`"
  2134. then
  2135.     echo shar: error transmitting "'nag.c'" '(should have been 29846 characters)'
  2136. fi
  2137. #    End of shell archive
  2138. exit 0
  2139.  
  2140.